package com.lanyou.esb.cook.proxy.service.impl;

import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.triggers.CronTriggerImpl;

import redis.clients.jedis.exceptions.JedisException;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.lanyou.esb.cook.proxy.redis.proxy.impl.ConcurrencyNumberJedisProxy;
import com.lanyou.esb.cook.proxy.service.ConcurrencyNumberService;

/**
 * 并发数服务
 * 
 * @author Davey.wu
 */
public class ConcurrencyNumberServiceImpl implements ConcurrencyNumberService,
		Job {
	/** 进入ESB的请求数 **/
	private static long INESB_REQ_NUMBER = 0;

	/** 出ESB的时间 **/
	private static BlockingQueue<Long> OUT_ESB_QUEUE = new LinkedBlockingQueue<>();
	/** 并发数Redis操作代理 **/
	private ConcurrencyNumberJedisProxy concurrencyNumberJedisProxy;

	private Scheduler scheduler;
	private String cron = "0/1 * * * * ?";
	private String name;
	private String group;
	private Class jobClass;
	private static boolean IS_CREATED = false;

	public ConcurrencyNumberServiceImpl() {
		// 每秒执行一次
		this.cron = "0/1 * * * * ?";
		this.name = "ConcurrencyNumberServiceImpl"+System.currentTimeMillis();
		this.group = "ConcurrencyNumberServiceImpl"+System.currentTimeMillis();
		this.jobClass = this.getClass();
	}

	@Override
	public void execute(JobExecutionContext context)
			throws JobExecutionException {
		ConcurrencyNumberServiceImpl service = (ConcurrencyNumberServiceImpl) context
				.getJobDetail().getJobDataMap().get("proxy");
		service.analysis();

	}

	@Override
	public void init() throws Exception {
		// 创建定时任务
		createJob();

	}

	/**
	 * 创建定时任务
	 * 
	 * @throws Exception
	 */
	private synchronized void createJob() throws Exception {
		if (IS_CREATED == true) {
			return;
		}
		IS_CREATED = true;
		System.out.println("定时任务------推送消息到REDIS");
		try {
			scheduler = new StdSchedulerFactory().getScheduler();
			// 创建LzstoneTimeTask的定时任务
			// JobDetail jobDetail = new JobDetailImpl(name, group, jobClass);
			JobDetail jobDetail = JobBuilder.newJob(jobClass)
					.withIdentity(name, group).build();
			JobDataMap jobMap = jobDetail.getJobDataMap();
			jobMap.put("proxy", this);
			// 目标 创建任务计划
			CronTrigger trigger = new CronTriggerImpl(name, group, cron);
			// CronTrigger trigger =
			// TriggerBuilder.newTrigger().withIdentity(name,group).
			scheduler = new StdSchedulerFactory().getScheduler();
			scheduler.scheduleJob(jobDetail, trigger);
			scheduler.start();
		} catch (Exception e) {
			System.out.println("定时任务------推送消息到REDIS，失败！");
			throw e;
		}
	}

	@Override
	public void destroy() {
		if(scheduler==null){
			return;
		}
		try {
			scheduler.shutdown();
		} catch (SchedulerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void pushToRedis(Date inEsbTime) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(inEsbTime);
		// 当前时间的毫秒数
		long millisecond = calendar.get(Calendar.MILLISECOND);
		try {
			// 当前秒的并发数减去当前时间的毫秒数
			if (millisecond > 0) {
				concurrencyNumberJedisProxy.setex(
						concurrencyNumberJedisProxy.generateKey(inEsbTime),
						-(double) millisecond / 1000.0);
			}
		} catch (JedisException e) {

		} catch (JsonProcessingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	@Override
	public void pushToRedis(Date inEsbTime, long millisecond) {
		try {
			concurrencyNumberJedisProxy.setex(
					concurrencyNumberJedisProxy.generateKey(inEsbTime),
					(double) millisecond / 1000.0);
		} catch (JedisException e) {

		} catch (JsonProcessingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	@Override
	public void analysis() {
		// 当前时间
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(new Date());
		calendar.set(Calendar.MILLISECOND, 0);
		long currentTime = calendar.getTimeInMillis();

		// 前一秒至当前秒并发数
		long pre2cur = INESB_REQ_NUMBER * 1000;
		// 当前秒至下一秒并发数调整值
		long cur2next = 0l;
		long outEsbTime = 0;
		long fix = 0;
		try {
			do {
				outEsbTime = OUT_ESB_QUEUE.remove();
				fix = Math.abs(currentTime - outEsbTime);
				if (fix > 1000) {
					fix = 1000;
				}
				// 出ESB的时间<=当前时间,前一秒至当前秒并发总时间减少
				if (outEsbTime <= currentTime) {
					pre2cur -= fix;
				}
				// 出ESB的时间>当前时间, 当前秒至下一秒并发数调整值增加
				else {
					cur2next += fix;
				}
				// 请求数递减
				decReqNumber();
			} while (true);
		} catch (Exception e) {
			// e.printStackTrace();
		}

		if (pre2cur < 0) {
			pre2cur = 0;
		}
		if (cur2next < 0) {
			cur2next = 0;
		}
		// 保存前一秒至当前秒时间
		calendar.add(Calendar.SECOND, -1);
		Date preDate = calendar.getTime();
		// 保存当前秒至下一秒时间
		calendar.add(Calendar.SECOND, 1);
		Date curDate = calendar.getTime();

		pushToRedis(preDate, pre2cur, curDate, cur2next);
	}

	public void pushToRedis(Date date1, long value1, Date date2, long value2) {
		try {
			concurrencyNumberJedisProxy.setex(
					concurrencyNumberJedisProxy.generateKey(date1),
					(double) value1 / 1000.0,
					concurrencyNumberJedisProxy.generateKey(date2),
					(double) value2 / 1000.0);
		} catch (Exception e) {
		}
	}

	/**
	 * 将时间加入阻塞队列
	 * 
	 * @param millisecond
	 */
	public synchronized static void addQueue(Date date) {
		Calendar calendar = Calendar.getInstance();
		OUT_ESB_QUEUE.add(calendar.getTimeInMillis());
	}

	/**
	 * 增加请求数
	 */
	public synchronized static void incReqNumber() {
		ConcurrencyNumberServiceImpl.INESB_REQ_NUMBER++;
	}

	/**
	 * 减少请求数
	 */
	public synchronized static void decReqNumber() {
		ConcurrencyNumberServiceImpl.INESB_REQ_NUMBER--;
		if (ConcurrencyNumberServiceImpl.INESB_REQ_NUMBER < 0) {
			ConcurrencyNumberServiceImpl.INESB_REQ_NUMBER = 0;
		}
	}

	public ConcurrencyNumberJedisProxy getConcurrencyNumberJedisProxy() {
		return concurrencyNumberJedisProxy;
	}

	public void setConcurrencyNumberJedisProxy(
			ConcurrencyNumberJedisProxy concurrencyNumberJedisProxy) {
		this.concurrencyNumberJedisProxy = concurrencyNumberJedisProxy;
	}

}
